package org.activiti.engine.impl.event.logger.handler;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import org.activiti.engine.delegate.event.ActivitiVariableEvent;
import org.activiti.engine.impl.variable.BooleanType;
import org.activiti.engine.impl.variable.DateType;
import org.activiti.engine.impl.variable.DoubleType;
import org.activiti.engine.impl.variable.IntegerType;
import org.activiti.engine.impl.variable.LongStringType;
import org.activiti.engine.impl.variable.LongType;
import org.activiti.engine.impl.variable.SerializableType;
import org.activiti.engine.impl.variable.ShortType;
import org.activiti.engine.impl.variable.StringType;
import org.activiti.engine.impl.variable.UUIDType;
import org.activiti.engine.impl.variable.VariableType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * @author Joram Barrez
 */
public abstract class VariableEventHandler extends AbstractDatabaseEventLoggerEventHandler {
	
	private static final Logger logger = LoggerFactory.getLogger(VariableEventHandler.class);
	
	public static final String TYPE_BOOLEAN = "boolean";
	public static final String TYPE_STRING = "string";
	public static final String TYPE_SHORT = "short";
	public static final String TYPE_INTEGER = "integer";
	public static final String TYPE_DOUBLE = "double";
	public static final String TYPE_LONG = "long";
	public static final String TYPE_DATE = "date";
	public static final String TYPE_UUID = "uuid";
	public static final String TYPE_JSON = "json";
	
	protected Map<String, Object> createData(ActivitiVariableEvent variableEvent) {
	  Map<String, Object> data = new HashMap<String, Object>();
		putInMapIfNotNull(data, Fields.NAME, variableEvent.getVariableName());
		putInMapIfNotNull(data, Fields.PROCESS_DEFINITION_ID, variableEvent.getProcessDefinitionId());
		putInMapIfNotNull(data, Fields.PROCESS_INSTANCE_ID, variableEvent.getProcessInstanceId());
		putInMapIfNotNull(data, Fields.EXECUTION_ID, variableEvent.getExecutionId());
		putInMapIfNotNull(data, Fields.VALUE, variableEvent.getVariableValue());
		
		VariableType variableType = variableEvent.getVariableType();
		if (variableType instanceof BooleanType) {
			
			putInMapIfNotNull(data, Fields.VALUE_BOOLEAN, (Boolean) variableEvent.getVariableValue());
			putInMapIfNotNull(data, Fields.VALUE, variableEvent.getVariableValue());
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_BOOLEAN);
			
		} else if (variableType instanceof StringType 
				|| variableType instanceof LongStringType) {
			
			putInMapIfNotNull(data, Fields.VALUE_STRING, (String) variableEvent.getVariableValue());
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_STRING);
			
		} else if (variableType instanceof ShortType) {
			
			Short value = (Short) variableEvent.getVariableValue();
			putInMapIfNotNull(data, Fields.VALUE_SHORT, value);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_SHORT);
			
			if (value != null) {
				putInMapIfNotNull(data, Fields.VALUE_INTEGER, value.intValue());
				putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
				putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
			}
			
		} else if (variableType instanceof IntegerType) {
			
			Integer value = (Integer) variableEvent.getVariableValue();
			putInMapIfNotNull(data, Fields.VALUE_INTEGER, value);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_INTEGER);
			
			if (value != null) {
				putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
				putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
			}
		
		} else if (variableType instanceof LongType) { 
		
			Long value = (Long) variableEvent.getVariableValue();
			putInMapIfNotNull(data, Fields.VALUE_LONG, value);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_LONG);
			
			if (value != null) {
				putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value.doubleValue());
			}
		
		} else if (variableType instanceof DoubleType) {
			
			Double value = (Double) variableEvent.getVariableValue();
			putInMapIfNotNull(data, Fields.VALUE_DOUBLE, value);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_DOUBLE);
			
			if (value != null) {
				putInMapIfNotNull(data, Fields.VALUE_INTEGER, value.intValue());
				putInMapIfNotNull(data, Fields.VALUE_LONG, value.longValue());
			}
		
		} else if (variableType instanceof DateType) {
			
			Date value = (Date) variableEvent.getVariableValue();
			putInMapIfNotNull(data, Fields.VALUE_DATE, value != null ? value.getTime() : null);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_DATE);
			
		} else if (variableType instanceof UUIDType) {
			
			String value = null;
			if (variableEvent.getVariableValue() instanceof UUID) {
				value = ((UUID) variableEvent.getVariableValue()).toString();
			} else {
				value = (String) variableEvent.getVariableValue();
			}
			
			putInMapIfNotNull(data, Fields.VALUE_UUID, value);
			putInMapIfNotNull(data, Fields.VALUE_STRING, value);
			putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_UUID);
			
		} else if (variableType instanceof SerializableType || 
				(variableEvent.getVariableValue() != null && (variableEvent.getVariableValue() instanceof Object) ) ) {
			
			// Last try: serialize it to json
			ObjectMapper objectMapper = new ObjectMapper();
			try {
	      String value = objectMapper.writeValueAsString(variableEvent.getVariableValue());
	    	putInMapIfNotNull(data, Fields.VALUE_JSON, value);
				putInMapIfNotNull(data, Fields.VARIABLE_TYPE, TYPE_JSON);
				putInMapIfNotNull(data, Fields.VALUE, value);
      } catch (JsonProcessingException e) {
	      // Nothing to do about it
      	logger.debug("Could not serialize variable value " + variableEvent.getVariableValue());
      }
			
		}
		
	  return data;
  }


}
